/**
 * @file STL_Thread.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.0.1
 * @date 2020-07-16
 * 
 * @coryright Copyright Sci-Tech Laboratories (c) 2020
 * 
 */

#include "STL_Thread.hpp"
#include <errno.h>

#include <cstring>
// #include <sys/prctl.h>

#define STLERR printf
#define STLLOG printf


namespace STL
{

    class ThreadImpl
    {
    public:
        ThreadImpl(const std::string &name, uint32_t stacksize);

        ~ThreadImpl();

        int start(const thread_proc_t &proc, const void *user_data);

        int stop();

        int detach();

        bool looping();

        void threadProc();

        int getThreadId();
        std::string getThreadName(){
            return m_threadName;
        };

    private:
        bool m_detach;
        bool m_looping; ///< 线程运行标志

        uint32_t m_stacksize;

        void *m_userdata;         ///<用户数据
        std::string m_threadName; ///<线程名称
        pthread_t m_tid;
        thread_proc_t m_proc; ///< 用户注册的执行体
    };

    static void *thread_proc(void *arg)
    {
        STLLOG("Entry thread proc, this: %p", arg);
        ThreadImpl *impl = (ThreadImpl *)arg;
        if (impl != nullptr)
        {
            impl->threadProc();
        }

        return nullptr;
    }

    ThreadImpl::ThreadImpl(const std::string &name, uint32_t stacksize)
        : m_looping(false), m_stacksize(stacksize), m_threadName(name)
    {
        STLLOG("create thread, this: %p", this);
    }

    ThreadImpl::~ThreadImpl()
    {
        STLLOG("destroy thread, this: %p", this);
    }

    int ThreadImpl::start(const thread_proc_t &proc, const void *user_data)
    {
        if (m_looping)
        {
            STLLOG("thread this: %p already created.", this);
            return -1;
        }

        m_proc = proc;
        m_userdata = const_cast<void *>(user_data);

        m_detach = false;

        ///< 先赋值,防止pthread_create函数未返回,先执行线程体
        m_looping = true;

        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setstacksize(&attr, m_stacksize);

        int r = pthread_create(&m_tid, &attr, thread_proc, this);
        if (r != 0)
        {
            STLLOG("create thread failed, errno: %s", strerror(errno));
            return -1;
        }

        pthread_attr_destroy(&attr);

        return 0;
    }

    int ThreadImpl::stop()
    {
        STLLOG("stop thread start, this: %p", this);
        m_looping = false;

        if (!m_detach)
        {
            pthread_join(m_tid, nullptr);
        }

        STLLOG("stop thread end, this: %p", this);
        return 0;
    }

    int ThreadImpl::detach()
    {
        m_detach = true;
        int err = pthread_detach(m_tid);
        if (err != 0)
        {
            STLLOG("detach thread failed, err: %s", strerror(err));
            return -1;
        }
        return 0;
    }

    bool ThreadImpl::looping()
    {
        return m_looping;
    }

    int ThreadImpl::getThreadId()
    {
        return int(m_tid);
    }

    void ThreadImpl::threadProc()
    {
        m_tid = pthread_self();

        // ::prctl(PR_SET_NAME, m_threadName.c_str());

        m_proc(m_userdata);
    }

    /////////////////////////Thread Warp/////////////////////////////

    Thread::Thread(const std::string &name, uint32_t stacksize)
    {
        m_impl = new ThreadImpl(name, stacksize);
    }

    Thread::~Thread()
    {
        if (m_impl != nullptr)
        {
            delete m_impl;
            m_impl = nullptr;
        }
    }

    int Thread::start(const thread_proc_t &proc, const void *user_data)
    {
        return m_impl->start(proc, user_data);
    }

    int Thread::stop()
    {
        return m_impl->stop();
    }

    int Thread::detach()
    {
        return m_impl->detach();
    }

    bool Thread::looping()
    {
        return m_impl->looping();
    }

    int Thread::getThreadId()
    {
        return m_impl->getThreadId();
    }

    std::string Thread::getThreadName(){
        return m_impl->getThreadName();
    }

} // namespace STL
